import redis
import sys
import os
import socket
import subprocess
import time

from database.rdb_conf import RedisConf

def check_ips_valid(ips = []):
    
    for ip in ips:
        
        if ip == "localhost":
            ip = "127.0.0.1"
        
        if not ip or '\x00' in ip:
            return False
        
        try:  
            res = socket.getaddrinfo(ip, 0, socket.AF_UNSPEC, socket.SOCK_STREAM, 0, socket.AI_NUMERICHOST)
            if not bool(res):
                return False
        except:
            return False
    
    return True

def check_port_open(host="", port = -1):
    
    if port == -1:
        return False
    
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    try:
        s.connect(host, port)
        s.shutdown(socket.SHUT_RDWR)
        return True
    except:
        return False


class RedisServer(object):
    
    def __init__(self):
        
        self.redis_server = ""
        
        self.configured_status = False
        self.runing_status = False
        self.dir = ""
        self.db_name = ""
        
        self.log_file = ""
        self.pid_file = ""
        self.out_file = ""
        self.conf_file = ""
        
        self.__bind_unix_socket = None
        self.__bind_hosts = []
        self.__bind_port = ""
        
    def is_configured(self):
        return self.configured_status
    
    def set_configured_status(self, flag=False):
        self.configured_status = flag   
    
    def is_runing(self):
        return self.runing_status
    
    def get_bind_port(self):
        return self.__bind_port
    
    def get_bind_hosts(self):
        return self.__bind_hosts
    
    def get_bind_unxi_socket(self):
        return self.__bind_unix_socket
    
    def set_bind_info(self, unix_socket=None, hosts=[], port=""):
        if unix_socket:
            self.__bind_unix_socket = unix_socket
        
        elif port:
            self.__bind_port = port
        
        if hosts:
            self.__bind_hosts = hosts
    
    def config_redis_server(self, redis_server="", name="", db_dir="", unix_socket=None, hosts=[], port=""):
         
        self.redis_server = redis_server
        self.dir = db_dir
         
        if hosts and check_ips_valid(hosts):
            self.__bind_hosts = hosts
        
        self.db_file = name + ".rdb"
        self.pid_file = os.path.join(self.dir, name + ".pid")
        self.log_file = os.path.join(self.dir, name + ".log")
        self.out_file = os.path.join(self.dir, name + ".out")
        self.conf_file = os.path.join(self.dir, name + ".conf")
        
        if not unix_socket and not port:
            port = "6379"
        
        if unix_socket:
            self.set_bind_info(unix_socket=unix_socket)
        else:
            self.set_bind_info(port=port)
        
        self.set_configured_status(flag=True)
    
    def __check_bind_port_available(self):
        
        if not self.__bind_port:
            return
        
        port_check_count = 30
        check_port = int(self.__bind_port)
        while port_check_count > 0:
            port_status = check_port_open(self.__bind_hosts, check_port)
            if not port_status:
                break
            port_check_count -= 1
            check_port += 10
        
        if port_check_count == 0:
            "not find available port"
            check_port = 7396
            
        self.__bind_port = str(check_port)
        
        
    
    def start_redis_server(self, env={}):
        
        if self.is_runing():
            return 
        
        if not self.is_configured():
            return 
        
        self.__check_bind_port_available()
        if not os.path.exists(self.conf_file):
            rdb_conf = RedisConf()
            conf_content = rdb_conf.generate(pid_file=self.pid_file, dir_path=self.dir, log_file=self.log_file, db_file=self.db_file, port=self.__bind_port, unix_socket=self.__bind_unix_socket)
                
            with open(self.conf_file, "w") as f:
                f.write(conf_content)
        
        
        command = [self.redis_server, self.conf_file]
        try:
            db_out = open(self.out_file, "w")
            ret = subprocess.check_call(command, shell=False, stdout=db_out)
        except:
            return
        
        redis_started = False
        time_out_count = 256
        while not redis_started and time_out_count > 0:
            
            db_t = redis.Redis(unix_socket_path=self.__bind_unix_socket, db=0)
            try:
                db_t.ping()
            except redis.ConnectionError:
                time.sleep(0.2)
                time_out_count -= 1
                continue
            except Exception as e:
                print(str(e))

            redis_started = True
        
        self.runing_status = True
        
        return
    
    def shut_down(self):
        
        rdb = redis.Redis(unix_socket_path=self.__bind_unix_socket)
        rdb.shutdown()
        self.runing_status = False
        